package org.dfl.plugin.editor.model;

import java.util.List;
import java.util.Map;

import org.dfl.core.model.Edge;
import org.dfl.core.model.TInputEdge;
import org.dfl.core.model.TOutputEdge;
import org.dfl.core.model.XMLLoaderException;
import org.dfl.core.model.listeners.EdgeListener;
import org.dfl.messages.Messages;
import org.dfl.plugin.dialogs.DialogsManager;
import org.dfl.plugin.editor.commands.CancelCommandRuntimeException;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Display;
import org.jdom.Element;


/**
 * editor's model element that represents a connection edge
 * @author Piotr Wlodarczyk
 *
 */
public class ConnectionModelElement extends ModelElement implements EdgeListener {
	/** Property ID to use when the list of outgoing connections is modified. */
	public static final String SELF_PROP = "Conn.self"; //$NON-NLS-1$

	private Edge edge;

	private Shape source;

	private Shape target;

	private String label = "";; //$NON-NLS-1$

	/** is already connected * */
	private boolean connected;

	public ConnectionModelElement(Shape source, Shape target) {
		super();
		// jesli laczymy z tranzycja i ma ona wiecej niz 1 wolne wejscie, to
		// trzeba je wybrac!
		if (target instanceof TransitionModelElement) {
			TransitionModelElement t = (TransitionModelElement) target;
			PlaceModelElement p = (PlaceModelElement) source;

			// wyciagniecie wolnych inputow pasujacych do miejsca
			// to, ze jakies sa wolne, powinno byc sprawdzone wczesniej
			List<String> freeInputs = getFreeInputs(p, t);

			if (freeInputs == null && t.getTransition().isInputNameNeeded()) {
				InputDialog dlg = new InputDialog(Display.getCurrent().getActiveShell(), Messages.ConnectionModelElement_inputBoxTitle, Messages.ConnectionModelElement_inputBoxText, "", null); //$NON-NLS-3$
				if (dlg.open() == Window.OK) {
					// User clicked OK; update the name
					setLabel(dlg.getValue());
				}
			} else if(!t.getTransition().isInputNameNeeded()){
				// do nothing if not needed
			} else if (freeInputs.size() == 1) {
				setLabel(freeInputs.iterator().next());
			} else {
				
				String res = (String) DialogsManager.openSelectDialog(Messages.TransitionInputSelectionDialog_title, Messages.TransitionInputSelectionDialog_title, freeInputs);
		        // null if user cancels
		        if (res!=null) {
		        	setLabel(res);
		        }else{
		        	throw new CancelCommandRuntimeException();
		        }
			}
		} else {
			setLabel(""); //$NON-NLS-1$
		}
		reconnect(source, target);
	}

	protected List<String> getFreeInputs(PlaceModelElement p, TransitionModelElement t) {
		return t.getTransition().getFreeInputs(p.getPlace().getTypeContainer().getType());
	}

	public ConnectionModelElement(Element elem, Map<String, Shape> modelMap) throws XMLLoaderException {
		String inputId = elem.getAttributeValue("source"); //$NON-NLS-1$
		String outputId = elem.getAttributeValue("target"); //$NON-NLS-1$
		source = modelMap.get(inputId);
		target = modelMap.get(outputId);
		if (source == null || target == null) {
			throw new XMLLoaderException(Messages.ConnectionModelElement_errorNoIO);
		}
		load(elem);
	}

	public Shape getSource() {
		return source;
	}

	public Shape getTarget() {
		return target;
	}

	public String getLabel() {
		return label;
	}

	public void setLabel(String label) {
		this.label = label;
	}

	public boolean reconnect() {
		return reconnect(false);
	}

	public boolean reconnect(boolean silent) {
		if (!connected) {
			PlaceModelElement placeModelElement;
			TransitionModelElement transitionModelElement;
			if (source instanceof PlaceModelElement) {
				if (edge == null)
					edge = getNewTInputEdge();
				((TInputEdge) edge).setLabel(getLabel());
				placeModelElement = (PlaceModelElement) source;
				transitionModelElement = (TransitionModelElement) target;
			} else {
				if (edge == null)
					edge = getNewTOutputEdge();
				placeModelElement = (PlaceModelElement) target;
				transitionModelElement = (TransitionModelElement) source;
			}

			try {
				edge.connect(placeModelElement.getPlace(), transitionModelElement.getTransition());
				// refreshMarkers();
				// connected=true;
				// placeModelElement.addConnection(this);
				// transitionModelElement.addConnection(this);
			} catch (Exception e) {
				if (!silent) {
					DialogsManager.openErrorDialog(e);
				}
				disconnect();
				return false;
			}
		}
		return true;
	}

	protected TInputEdge getNewTInputEdge() {
		TInputEdge e = new TInputEdge();
		e.addListener(this);
		return e;

	}

	protected TOutputEdge getNewTOutputEdge() {
		TOutputEdge e = new TOutputEdge();
		e.addListener(this);
		return e;
	}

	public void setNoneProperty() {
		getEdge().resetProperties();
		fireSelfPropChanged();
	}

	public void setTrueProperty() {
		try {
			getEdge().setNeededTrue();
		} catch (Exception e) {
			getEdge().resetProperties();
			DialogsManager.openErrorDialog(e);
		}
		fireSelfPropChanged();
	}

	public void setFalseProperty() {
		try {
			getEdge().setNeededFalse();
		} catch (Exception e) {
			getEdge().resetProperties();
			DialogsManager.openErrorDialog(e);
		}
		fireSelfPropChanged();
	}

	public void setEmptyProperty() {
		try {
			getEdge().setNeededEmpty();
		} catch (Exception e) {
			getEdge().resetProperties();
			DialogsManager.openErrorDialog(e);
		}
		fireSelfPropChanged();
	}

	public void setNotemptyProperty() {
		try {
			getEdge().setNeededNotempty();
		} catch (Exception e) {
			getEdge().resetProperties();
			DialogsManager.openErrorDialog(e);
		}
		fireSelfPropChanged();
	}

	public void reconnect(Shape newSource, Shape newTarget) {
		disconnect();

		source = newSource;
		target = newTarget;

		reconnect();
	}

	public void disconnect() {
		if (connected) {
			edge.disconnect();
			// refreshMarkers();
			// connected = false;
			// edge = null;
			// source.removeConnection(this);
			// target.removeConnection(this);
		}
	}

	public Edge getEdge() {
		return edge;
	}

	public String getInputName() {
		if (edge instanceof TInputEdge) {
			return ((TInputEdge) edge).getLabel();
		} else {
			return ""; //$NON-NLS-1$
		}
	}

	public void load(Element elem) throws XMLLoaderException {
		// musi byc najpierw ustawiony source i target!!
		Element tsElem = elem.getChild("toolspecific"); //$NON-NLS-1$
		if (tsElem != null) {
			String n = tsElem.getChildText("name"); //$NON-NLS-1$
			setLabel(n == null ? "" : n); //$NON-NLS-1$
		}
		reconnect();
		edge.load(elem); // wgranie danych polaczenia (juz bez bezposredniego
							// laczenia)
	}

	public Element save() {
		return edge.save();
	}

	public void edgeConnected(Edge e) {
		connected = true;

		PlaceModelElement placeModelElement;
		TransitionModelElement transitionModelElement;
		if (source instanceof PlaceModelElement) {
			placeModelElement = (PlaceModelElement) source;
			transitionModelElement = (TransitionModelElement) target;
		} else {
			placeModelElement = (PlaceModelElement) target;
			transitionModelElement = (TransitionModelElement) source;
		}

		try {
			placeModelElement.addConnection(this);
			transitionModelElement.addConnection(this);
		} catch (Exception e1) {
			DialogsManager.openErrorDialog(e1);
			disconnect();
		}
		refreshMarkers();
	}

	public void edgeDisconnected(Edge e) {
		connected = false;
		// edge = null;
		source.removeConnection(this);
		target.removeConnection(this);
		refreshMarkers();
	}

	public void fireSelfPropChanged() {
		if(isRefreshDiagramEnabled()){
			firePropertyChange(SELF_PROP, this, null);
			getSource().modelStructureChanged();
		}
	}
	
	@Override
	public void refreshMarkers() {
		if (isRefreshDiagramEnabled()) {
			super.refreshMarkers();
			source.refreshMarkers();
			target.refreshMarkers();
		}
	}

	@Override
	public DflDiagram getDiagram() {
		return getTarget().getDiagram();
	}
	
	@Override
	public void setRefreshDiagramEnabled(boolean propertyFireEnabled) {
		super.setRefreshDiagramEnabled(propertyFireEnabled);
		source.setRefreshDiagramEnabled(propertyFireEnabled);
		target.setRefreshDiagramEnabled(propertyFireEnabled);
	}
}
